﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Skugo.Shared;
using System.IO;

namespace Skugo
{
    public class TextEditorEvent : EventArgs
    {
        public String Text;
        public TextEditor Editor;
    }

    public class AutoCompleteEvent : EventArgs
    {
        public Int32 Line;
        public List<String> IdentifiersBefore;
        public TextEditor Editor;
        public Boolean IsCtrlSpace;
    }

    public class Completion
    {
        public Icon Icon;
        public String Text;
        public String Description;
    }

    public class Overload
    {
        public String Signature;
        public String Description;
    }

    public class TextEditor : DockablePlugin
    {
        /// <summary>
        /// The type of message we send or receieve.
        /// </summary>
        enum MessageType : byte
        {
            SetText,
            SetSyntaxHighlighting,
            SetMemberAccessTrigger,
            SetCallTrigger,
            ShowAutoComplete,
            ShowAutoCompleteUsingDocumentWords,
            ShowCallTips,
            TextChanged,
            TextEntered,
            TriggerMemberAccess,
            TriggerCall,
        }

        /// <summary>
        /// The name of the remote type we wish to instantiate.
        /// </summary>
        private const String RemoteType = "Skugo.StandardPlugins.TextEditor";

        public event EventHandler<TextEditorEvent> OnTextChanged;
        public event EventHandler<TextEditorEvent> OnTextEntered;
        public event EventHandler<AutoCompleteEvent> OnTriggerMemberAccess;
        public event EventHandler<AutoCompleteEvent> OnTriggerCall;


        public TextEditor(Client client, String displayName) :
            base(RemoteType, client, displayName)
        {
        }

        public void SetText(String text)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.SetText);
            outgoing.WriteAsciiString(text);

            this.Send(stream.ToArray());
        }

        public void SetSyntaxHighlighting(String xml)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.SetSyntaxHighlighting);
            outgoing.WriteAsciiString(xml);

            this.Send(stream.ToArray());
        }

        public void SetMemberAccessTrigger(Char character)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.SetMemberAccessTrigger);
            outgoing.Write(character);

            this.Send(stream.ToArray());
        }

        public void SetCallTrigger(Char character)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.SetCallTrigger);
            outgoing.Write(character);

            this.Send(stream.ToArray());
        }

        public void ShowAutoComplete(List<Completion> options)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.ShowAutoComplete);
            outgoing.Write((Int32)options.Count);

            foreach (var option in options)
            {
                outgoing.WriteAsciiString(option.Text);
                outgoing.WriteAsciiString(option.Description);
                outgoing.Write((Int32)option.Icon);
            }

            this.Send(stream.ToArray());
        }

        public void ShowAutoComplete(List<String> options)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.ShowAutoComplete);
            outgoing.Write((Int32)options.Count);

            foreach (var option in options)
            {
                outgoing.WriteAsciiString(option);
                outgoing.WriteAsciiString(String.Empty);
                outgoing.Write((Int32)Icon.GreenCircle);
            }

            this.Send(stream.ToArray());
        }

        public void ShowAutoCompleteUsingDocumentWords()
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.ShowAutoCompleteUsingDocumentWords);
            this.Send(stream.ToArray());
        }

        public void ShowCallTips(List<Overload> overloads)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.ShowCallTips);
            outgoing.Write((Int32)overloads.Count);

            foreach (var overload in overloads)
            {
                outgoing.WriteAsciiString(overload.Signature);
                outgoing.WriteAsciiString(overload.Description);
            }

            this.Send(stream.ToArray());
        }

        public void ShowCallTips(List<String> signatures)
        {
            // Create a memory stream to write data to
            MemoryStream stream = new MemoryStream();

            // Create a binary writer to write the data
            BinaryWriter outgoing = new BinaryWriter(stream);

            outgoing.Write((Byte)MessageType.ShowCallTips);
            outgoing.Write((Int32)signatures.Count);

            foreach (var signature in signatures)
            {
                outgoing.WriteAsciiString(signature);
                outgoing.WriteAsciiString(String.Empty);
            }

            this.Send(stream.ToArray());
        }

        protected override void OnReceive(byte[] data)
        {
            var stream = new MemoryStream(data);

            var incoming = new BinaryReader(stream);

            var message = (MessageType)incoming.ReadByte();

            switch (message)
            {
                case MessageType.TextChanged:
                {
                    var text = incoming.ReadAsciiString();

                    if (this.OnTextChanged != null)
                    {
                        var e = new TextEditorEvent();
                        e.Text = text;
                        e.Editor = this;
                        this.OnTextChanged(this, e);
                    }
                    break;
                }
                case MessageType.TextEntered:
                {
                    var entered = incoming.ReadAsciiString();

                    if (this.OnTextEntered != null)
                    {
                        var e = new TextEditorEvent();
                        e.Text = entered;
                        e.Editor = this;
                        this.OnTextEntered(this, e);
                    }
                    break;
                }
                case MessageType.TriggerMemberAccess:
                case MessageType.TriggerCall:
                {
                    var line = incoming.ReadInt32();
                    var isCtrlSpace = incoming.ReadBoolean();
                    var identifierCount = incoming.ReadInt32();

                    var identifiers = new List<String>();

                    for (var i = 0; i < identifierCount; ++i)
                    {
                        var identifier = incoming.ReadAsciiString();
                        identifiers.Add(identifier);
                    }

                    var e = new AutoCompleteEvent();
                    e.Line = line;
                    e.IdentifiersBefore = identifiers;
                    e.Editor = this;
                    e.IsCtrlSpace = isCtrlSpace;

                    if (message == MessageType.TriggerMemberAccess)
                    {
                        if (this.OnTriggerMemberAccess != null)
                        {
                            this.OnTriggerMemberAccess(this, e);
                        }
                    }
                    else if (message == MessageType.TriggerCall)
                    {
                        if (this.OnTriggerCall != null)
                        {
                            this.OnTriggerCall(this, e);
                        }
                    }
                    break;
                }
            }
        }
    }
}
